

#include <ctime>
#include <random>
#include <cassert>
#include <algorithm>

#include <string>
#include <iomanip>
#include <sstream>
#include <fstream>
#include <iostream>

#include <tuple>
#include <deque>
#include <vector>
#include <utility>
#include <functional>
#include <type_traits>

#include <concepts>

#include "Vector2D.hpp"

/**
 * @file Utils.hpp
 * @brief 提供小工具
 * @author Creeper
 * @date 2021-07-02 (Modified: 2021-07-21)
 * @version V1.1
 *
 * @note
 * 目前提供了以下内容
 * -# 仿Python日志
 * -# 随机数生成器
 * -# 数学小工具
 * -# 字符串输出以及处理
 **/

typedef std::pair<int, int> PII;

struct Logger
{
    Logger(){};
    Logger(Logger &) = delete;
    Logger &operator=(const Logger &) = delete;
    std::ofstream lger;
    char s[54];
    void init();
    std::string getTime();

    // true/false : 是否送入控制台
    template <typename T>
    inline void sendLog(const std::string &logLevel, const T &tar, const std::string &p, std::true_type);
    template <typename T>
    inline void sendLog(const std::string &logLevel, const T &tar, const std::string &p, std::false_type);
    template <typename T>
    inline void Critical(const T &tar, const std::string &p = "");
    template <typename T>
    inline void Error(const T &tar, const std::string &p = "");
    template <typename T>
    inline void Warning(const T &tar, const std::string &p = "");
    template <typename T>
    inline void Info(const T &tar, const std::string &p = "");
    template <typename T>
    inline void Debug(const T &tar, const std::string &p = "");
    void _Critical(const std::string &tar);
    void _Error(const std::string &tar);
    void _Warning(const std::string &tar);
    void _Info(const std::string &tar);
    void _Debug(const std::string &tar);

    ~Logger()
    {
        lger.close();
    }
};

namespace NoBody
{
    extern Logger logger;
}

// _Logger 类通用，输出的时候带上一个logEnd可以向Logger输出一次，否则的话就不向Logger输出，产生积累效果
struct LogEnd
{
};

extern LogEnd logEnd;

struct _Logger
{
    std::function<void(const std::string &)> target;
    _Logger(std::function<void(const std::string &)> tar) : target(tar){};
    std::stringstream ss;
    int disabled; ///< 是否禁用（注意别忘了打开QWQ
    inline void activate(bool force = false)
    {
        if (force)
            disabled = 0;
        else
            ++disabled;
    }
    inline void deactivate(bool force = false)
    {
        if (force)
            disabled = -1;
        else
            --disabled;
    }
    inline void call()
    {
        target(ss.str());
    }
    inline void clear()
    {
        ss.str("");
    }
};

extern _Logger dD, iI, wW, eE, cC;

// 用于禁用Logger的，在一个作用域中接收一个_Logger，然后进入作用域的时候禁用调试输出，离开的时候自动解除，自动对调用栈进行推导
struct DisableLogger
{
    _Logger &tar;
    DisableLogger(_Logger &_tar) : tar(_tar)
    {
        tar.deactivate(false);
    }
    ~DisableLogger()
    {
        tar.activate(false);
    }
};

template <typename T>
_Logger &operator<<(_Logger &os, const T &tar)
{
    if (os.disabled)
        return os;
    os.ss << tar;
#ifdef LOG_ADD_SPACE
    os.ss << ' ';
#endif
    return os;
}

template <>
_Logger &operator<<(_Logger &os, const LogEnd &);

template <typename T1, typename T2>
std::ostream &operator<<(std::ostream &os, const std::pair<T1, T2> &p)
{
    os << "(" << p.first << "," << p.second << ")";
    return os;
}

template <typename T1, typename T2, typename T3>
std::ostream &operator<<(std::ostream &os, const std::tuple<T1, T2, T3> &t)
{
    os << '(' << std::get<0>(t) << "," << std::get<1>(t) << "," << std::get<2>(t) << ')';
    return os;
}

template <typename T>
std::ostream &operator<<(std::ostream &os, const std::vector<T> &c)
{
    // os << typeid(c).name() << ": ["; // 未来适配特定容器的时候改成这个
    os << typeid(T).name() << ": [";
    for (const auto &it : c)
        os << it << ',';
    os << "]";
    return os;
}

template <typename T>
std::ostream &operator<<(std::ostream &os, const std::deque<T> &c)
{
    // os << typeid(c).name() << ": ["; // 未来适配特定容器的时候改成这个
    os << typeid(T).name() << ": [";
    for (const auto &it : c)
        os << it << ',';
    os << "]";
    return os;
}

std::string to_string(double tar, int width = -1, int precision = -1);

template <typename T1, typename T2>
std::string to_string(const std::pair<T1, T2> &p)
{
    return "(" + p.first + ',' + p.second + ')';
}

using ::to_string;

/**
 * 虽然你会发现有些函数不能接收int
 * 不过这是故意的呢QWQ
 * 呐呐呐
 */
struct _Random
{

    std::mt19937 Generator;
    std::mt19937_64 Generator_64;

    _Random(unsigned int seed = 19260817 /*std::time(NULL)*/) : Generator(seed), Generator_64(seed)
    {
        srand(seed);
    };

    inline unsigned rand();
    inline unsigned rand_64();
    inline unsigned rand(const unsigned &up);                                                        ///< [0, up)
    inline unsigned long long rand_64(const unsigned long long &up);                                    ///< [0, up)
    inline unsigned randInt(const unsigned &up);                                                     ///< [1, up]
    inline unsigned long long randInt_64(const unsigned long long &up);                                 ///< [1, up]
    inline unsigned randInt(const unsigned &down, const unsigned &up);                               ///< [down, up]
    inline unsigned long long randInt_64(const unsigned long long &down, const unsigned long long &up); ///< [down, up]
    inline double randDouble();                                                                      ///< [0, 1)均匀
    inline double randDouble(const double &down, const double &up);                                  ///< [down, up]
    inline unsigned randBit();                                                                       ///< 0 / 1
    template <typename Iterator_Type>
    inline void randomShuffle(Iterator_Type __first, Iterator_Type __last);
    template <typename Iterator_Type>
    inline Iterator_Type randomPicker(Iterator_Type __first, Iterator_Type __last);
};

template <typename value_type, size_t MAXN, size_t MAXM>
struct ARRVEC
{
    value_type a[MAXN][MAXM];
    ARRVEC() : a() {}
    inline void clear(int tar = 0)
    {
        memset(a, tar, sizeof(a));
    }
    inline value_type *operator[](int ind0)
    {
        return a[ind0];
    }
    inline value_type &operator[](const PII &ind)
    {
        return a[ind.first][ind.second];
    }
    inline value_type &operator[](const Vec2i &ind)
    {
        return a[ind.x][ind.y];
    }
    inline const value_type *operator[](int ind0) const
    {
        return a[ind0];
    }
    inline const value_type &operator[](const PII &ind) const
    {
        return a[ind.first][ind.second];
    }
    inline const value_type &operator[](const Vec2i &ind) const
    {
        return a[ind.x][ind.y];
    }
};

extern _Random Random;

template <typename T>
T sqr(T tar)
{
    return tar * tar;
}

template <typename T, typename R>
requires std::is_integral<R>::value
    T
    sqr(T tar, R tim)
{
    T ret = 1;
    while (tim)
    {
        if (tim & 1)
            ret = ret * tar;
        tar *= tar;
        tim >>= 1;
    }
    return ret;
}

/**
 * @brief 字符串是否以某个家伙结尾
 * @param main 主串
 * @param pattern 模式串
 * @return 是/否
*/
bool endWith(const std::string &main, const std::string &pattern);
